#include <pcap.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>

#define MAX_PACKETS 10000

// Linux cooked capture v2 头部结构
struct sll2_header {
    uint16_t protocol_type;
    uint16_t reserved;
    uint32_t interface_index;
    uint16_t arphrd_type;
    uint8_t packet_type;
    uint8_t addr_len;
    uint64_t addr;
};

typedef struct {
    char username[64];
    char password[64];
    char src_ip[16];
    char dst_ip[16];
    time_t timestamp;
} ftp_session_t;

ftp_session_t current_session = {0};

// 写入FTP登录日志
void write_ftp_log(FILE *file, const char *timestamp, const char *src_ip, 
                  const char *dst_ip, const char *username, const char *password, 
                  const char *status) {
    fprintf(file, "%s,-,%s,-,%s,%s,%s,%s\n", 
            timestamp, src_ip, dst_ip, username, password, status);
    fflush(file);
    printf("[FTP登录] %s -> %s, 用户=%s, 密码=%s, 状态=%s\n", 
           src_ip, dst_ip, username, password, status);
}

// 获取时间戳字符串
void get_timestamp(char *buffer, size_t size) {
    time_t now = time(NULL);
    struct tm *tm_info = localtime(&now);
    strftime(buffer, size, "%Y-%m-%d %H:%M:%S", tm_info);
}

// 解析FTP数据
void parse_ftp_data(const u_char *data, int data_len, const char *src_ip, 
                   const char *dst_ip, int is_request) {
    char ftp_data[1024];
    char timestamp[20];
    
    get_timestamp(timestamp, sizeof(timestamp));
    
    // 确保数据以null结尾
    int copy_len = data_len < sizeof(ftp_data)-1 ? data_len : sizeof(ftp_data)-1;
    memcpy(ftp_data, data, copy_len);
    ftp_data[copy_len] = '\0';
    
    printf("[FTP数据] %s -> %s, 长度=%d, 内容: ", src_ip, dst_ip, data_len);
    // 安全地打印内容（避免控制字符）
    for(int i = 0; i < copy_len && i < 50; i++) {
        if(ftp_data[i] >= 32 && ftp_data[i] < 127) {
            putchar(ftp_data[i]);
        } else {
            printf("\\x%02x", (unsigned char)ftp_data[i]);
        }
    }
    printf("\n");
    
    // 查找FTP命令 (从客户端发出的请求)
    if (is_request) {
        if (strstr(ftp_data, "USER ") == ftp_data) {
            // 提取用户名
            char *user_start = ftp_data + 5;  // 跳过"USER "
            char *user_end = strpbrk(user_start, "\r\n");
            if (user_end) {
                *user_end = '\0';
                strncpy(current_session.username, user_start, sizeof(current_session.username)-1);
                strcpy(current_session.src_ip, src_ip);
                strcpy(current_session.dst_ip, dst_ip);
                current_session.timestamp = time(NULL);
                printf("[检测到用户名] %s\n", current_session.username);
            }
        }
        else if (strstr(ftp_data, "PASS ") == ftp_data) {
            // 提取密码
            char *pass_start = ftp_data + 5;  // 跳过"PASS "
            char *pass_end = strpbrk(pass_start, "\r\n");
            if (pass_end) {
                *pass_end = '\0';
                strncpy(current_session.password, pass_start, sizeof(current_session.password)-1);
                printf("[检测到密码] %s\n", current_session.password);
            }
        }
    } else {
        // 服务器响应
        if (strstr(ftp_data, "230 ") == ftp_data) {
            // 登录成功
            if (strlen(current_session.username) > 0) {
                FILE *log_file = fopen("ftp_logins.csv", "a");
                if (log_file) {
                    write_ftp_log(log_file, timestamp, current_session.src_ip, 
                                 current_session.dst_ip, current_session.username, 
                                 current_session.password, "SUCCEED");
                    fclose(log_file);
                } else {
                    printf("[错误] 无法打开日志文件\n");
                }
                memset(&current_session, 0, sizeof(current_session));
            }
        }
        else if (strstr(ftp_data, "530 ") == ftp_data) {
            // 登录失败
            if (strlen(current_session.username) > 0) {
                FILE *log_file = fopen("ftp_logins.csv", "a");
                if (log_file) {
                    write_ftp_log(log_file, timestamp, current_session.src_ip, 
                                 current_session.dst_ip, current_session.username, 
                                 current_session.password, "FAILED");
                    fclose(log_file);
                } else {
                    printf("[错误] 无法打开日志文件\n");
                }
                memset(&current_session, 0, sizeof(current_session));
            }
        }
    }
}

// 处理LINUX_SLL2格式的数据包
void packet_handler(u_char *user_data, const struct pcap_pkthdr *pkthdr, const u_char *packet) {
    // LINUX_SLL2头部长度为16字节
    struct sll2_header *sll2 = (struct sll2_header*)packet;
    
    // 跳过SLL2头部
    const u_char *ip_packet = packet + 16;
    int ip_start = 16;
    
    // 检查IP头
    struct iphdr *ip_header = (struct iphdr*)ip_packet;
    
    // 验证IP版本
    if ((ip_header->version != 4) || (ip_header->ihl < 5)) {
        return; // 不是IPv4或头部长度无效
    }
    
    char src_ip[16], dst_ip[16];
    strcpy(src_ip, inet_ntoa(*(struct in_addr*)&ip_header->saddr));
    strcpy(dst_ip, inet_ntoa(*(struct in_addr*)&ip_header->daddr));
    
    // 检查是否为TCP包
    if (ip_header->protocol == IPPROTO_TCP) {
        int ip_header_len = ip_header->ihl * 4;
        
        // 检查数据包长度
        if (pkthdr->len < ip_start + ip_header_len + sizeof(struct tcphdr)) {
            return;
        }
        
        struct tcphdr *tcp_header = (struct tcphdr*)(ip_packet + ip_header_len);
        
        int src_port = ntohs(tcp_header->source);
        int dst_port = ntohs(tcp_header->dest);
        
        // 检查是否为FTP端口(21)
        if (src_port == 21 || dst_port == 21) {
            printf("[捕获FTP] %s:%d -> %s:%d\n", src_ip, src_port, dst_ip, dst_port);
            
            // 计算TCP数据长度
            int tcp_header_len = tcp_header->doff * 4;
            int data_len = pkthdr->len - (ip_start + ip_header_len + tcp_header_len);
            
            if (data_len > 0) {
                // 获取TCP数据
                const u_char *tcp_data = ip_packet + ip_header_len + tcp_header_len;
                
                // 判断是请求还是响应
                int is_request = (src_port != 21); // 如果不是从端口21发出的，就是请求
                parse_ftp_data(tcp_data, data_len, src_ip, dst_ip, is_request);
            }
        }
    }
}

int main() {
    pcap_t *handle;
    char errbuf[PCAP_ERRBUF_SIZE];
    struct bpf_program fp;
    char filter_exp[] = "tcp port 21";
    
    printf("=== Linux SLL2 FTP协议分析器 ===\n");
    
    // 打开any设备（使用LINUX_SLL2格式）
    handle = pcap_open_live("any", BUFSIZ, 1, 1000, errbuf);
    if (handle == NULL) {
        fprintf(stderr, "无法打开设备: %s\n", errbuf);
        return 1;
    }
    
    // 检查数据链路类型
    int dlt = pcap_datalink(handle);
    printf("数据链路类型: %d (%s)\n", dlt, pcap_datalink_val_to_name(dlt));
    
    // 编译和设置过滤器
    if (pcap_compile(handle, &fp, filter_exp, 0, PCAP_NETMASK_UNKNOWN) == -1) {
        fprintf(stderr, "无法编译过滤器: %s\n", pcap_geterr(handle));
        pcap_close(handle);
        return 1;
    }
    
    if (pcap_setfilter(handle, &fp) == -1) {
        fprintf(stderr, "无法设置过滤器: %s\n", pcap_geterr(handle));
        pcap_close(handle);
        return 1;
    }
    
    // 创建FTP日志文件
    FILE *log_file = fopen("ftp_logins.csv", "w");
    if (!log_file) {
        fprintf(stderr, "无法创建FTP日志文件\n");
        pcap_close(handle);
        return 1;
    }
    
    // 写入CSV头部
    fprintf(log_file, "时间,源MAC,源IP,目标MAC,目标IP,登录名,口令,成功与否\n");
    fflush(log_file);
    fclose(log_file);
    
    printf("开始侦听FTP流量(端口21)...\n");
    printf("按Ctrl+C停止侦听\n");
    
    // 开始捕获数据包
    pcap_loop(handle, MAX_PACKETS, packet_handler, NULL);
    
    // 清理资源
    pcap_close(handle);
    
    printf("FTP侦听结束\n");
    return 0;
}
